package com.anlogic.sdk.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.nio.charset.StandardCharsets;

/**
 * Encryption utility class
 * Provides common encryption and decryption algorithm implementations
 */
public class EncryptUtils {
    
    // AES algorithm related constants
    private static final String AES_ALGORITHM = "AES";
    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    
    // DES algorithm related constants
    private static final String DES_ALGORITHM = "DES";
    private static final String DES_TRANSFORMATION = "DES/CBC/PKCS5Padding";
    
    // RSA algorithm related constants
    private static final String RSA_ALGORITHM = "RSA";
    private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
    
    /**
     * AES encryption
     * @param plaintext plaintext to encrypt
     * @param key encryption key (16, 24 or 32 bytes)
     * @return Base64 encoded encrypted string
     */
    public static String aesEncrypt(String plaintext, String key) {
        try {
            // Generate secret key
            SecretKeySpec secretKey = new SecretKeySpec(adjustKeyLength(key, 16), AES_ALGORITHM);
            
            // Generate random IV
            byte[] iv = new byte[16];
            new SecureRandom().nextBytes(iv);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            
            // Create cipher
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
            
            // Encrypt
            byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            
            // Combine IV and encrypted data
            byte[] result = new byte[iv.length + encrypted.length];
            System.arraycopy(iv, 0, result, 0, iv.length);
            System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
            
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException("AES encryption failed", e);
        }
    }
    
    /**
     * AES decryption
     * @param ciphertext encrypted text (Base64 encoded)
     * @param key decryption key
     * @return decrypted plaintext
     */
    public static String aesDecrypt(String ciphertext, String key) {
        try {
            // Decode Base64
            byte[] data = Base64.getDecoder().decode(ciphertext);
            
            // Extract IV
            byte[] iv = new byte[16];
            System.arraycopy(data, 0, iv, 0, 16);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            
            // Extract encrypted data
            byte[] encrypted = new byte[data.length - 16];
            System.arraycopy(data, 16, encrypted, 0, encrypted.length);
            
            // Generate secret key
            SecretKeySpec secretKey = new SecretKeySpec(adjustKeyLength(key, 16), AES_ALGORITHM);
            
            // Create cipher for decryption
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
            
            // Decrypt
            byte[] decrypted = cipher.doFinal(encrypted);
            
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("AES decryption failed", e);
        }
    }
    
    /**
     * DES encryption
     * @param plaintext plaintext to encrypt
     * @param key encryption key (8 bytes)
     * @return Base64 encoded encrypted string
     */
    public static String desEncrypt(String plaintext, String key) {
        try {
            // Generate secret key
            SecretKeySpec secretKey = new SecretKeySpec(adjustKeyLength(key, 8), DES_ALGORITHM);
            
            // Generate random IV
            byte[] iv = new byte[8];
            new SecureRandom().nextBytes(iv);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            
            // Create cipher
            Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
            
            // Encrypt
            byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            
            // Combine IV and encrypted data
            byte[] result = new byte[iv.length + encrypted.length];
            System.arraycopy(iv, 0, result, 0, iv.length);
            System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
            
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException("DES encryption failed", e);
        }
    }
    
    /**
     * DES decryption
     * @param ciphertext encrypted text (Base64 encoded)
     * @param key decryption key
     * @return decrypted plaintext
     */
    public static String desDecrypt(String ciphertext, String key) {
        try {
            // Decode Base64
            byte[] data = Base64.getDecoder().decode(ciphertext);
            
            // Extract IV
            byte[] iv = new byte[8];
            System.arraycopy(data, 0, iv, 0, 8);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            
            // Extract encrypted data
            byte[] encrypted = new byte[data.length - 8];
            System.arraycopy(data, 8, encrypted, 0, encrypted.length);
            
            // Generate secret key
            SecretKeySpec secretKey = new SecretKeySpec(adjustKeyLength(key, 8), DES_ALGORITHM);
            
            // Create cipher for decryption
            Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
            
            // Decrypt
            byte[] decrypted = cipher.doFinal(encrypted);
            
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("DES decryption failed", e);
        }
    }
    
    /**
     * Generate RSA key pair
     * @param keySize key length (1024, 2048, 4096, etc.)
     * @return RSA key pair
     */
    public static KeyPair generateRSAKeyPair(int keySize) {
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyGen.initialize(keySize);
            return keyGen.generateKeyPair();
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate RSA key pair", e);
        }
    }
    
    /**
     * RSA public key encryption
     * @param plaintext plaintext to encrypt
     * @param publicKey public key
     * @return Base64 encoded encrypted string
     */
    public static String rsaEncryptWithPublicKey(String plaintext, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException("RSA public key encryption failed", e);
        }
    }
    
    /**
     * RSA private key decryption
     * @param ciphertext encrypted text (Base64 encoded)
     * @param privateKey private key
     * @return decrypted plaintext
     */
    public static String rsaDecryptWithPrivateKey(String ciphertext, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] data = Base64.getDecoder().decode(ciphertext);
            byte[] decrypted = cipher.doFinal(data);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("RSA private key decryption failed", e);
        }
    }
    
    /**
     * MD5 hash
     * @param input input string
     * @return MD5 hash value (32-bit lowercase hexadecimal)
     */
    public static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not available", e);
        }
    }
    
    /**
     * SHA-1 hash
     * @param input input string
     * @return SHA-1 hash value (40-bit lowercase hexadecimal)
     */
    public static String sha1(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-1 algorithm not available", e);
        }
    }
    
    /**
     * SHA-256 hash
     * @param input input string
     * @return SHA-256 hash value (64-bit lowercase hexadecimal)
     */
    public static String sha256(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not available", e);
        }
    }
    
    /**
     * SHA-512 hash
     * @param input input string
     * @return SHA-512 hash value (128-bit lowercase hexadecimal)
     */
    public static String sha512(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-512 algorithm not available", e);
        }
    }
    
    /**
     * Base64 encoding
     * @param input input string
     * @return Base64 encoded string
     */
    public static String base64Encode(String input) {
        return Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
    }
    
    /**
     * Base64 decoding
     * @param input Base64 encoded string
     * @return decoded string
     */
    public static String base64Decode(String input) {
        try {
            byte[] decoded = Base64.getDecoder().decode(input);
            return new String(decoded, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("Base64 decoding failed", e);
        }
    }
    
    /**
     * Generate random AES key
     * @param keySize key length (128, 192 or 256 bits)
     * @return Base64 encoded key
     */
    public static String generateAESKey(int keySize) {
        try {
            KeyGenerator keyGen = KeyGenerator.getInstance(AES_ALGORITHM);
            keyGen.init(keySize);
            SecretKey secretKey = keyGen.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate AES key", e);
        }
    }
    
    /**
     * Generate random DES key
     * @return Base64 encoded key
     */
    public static String generateDESKey() {
        try {
            KeyGenerator keyGen = KeyGenerator.getInstance(DES_ALGORITHM);
            keyGen.init(56); // DES uses 56-bit key
            SecretKey secretKey = keyGen.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate DES key", e);
        }
    }
    
    /**
     * Adjust key length
     * @param key original key
     * @param targetLength target length
     * @return adjusted key byte array
     */
    private static byte[] adjustKeyLength(String key, int targetLength) {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        byte[] result = new byte[targetLength];
        
        if (keyBytes.length >= targetLength) {
            System.arraycopy(keyBytes, 0, result, 0, targetLength);
        } else {
            System.arraycopy(keyBytes, 0, result, 0, keyBytes.length);
            // Fill remaining with zeros
            for (int i = keyBytes.length; i < targetLength; i++) {
                result[i] = 0;
            }
        }
        
        return result;
    }
    
    /**
     * Convert byte array to hexadecimal string
     * @param bytes byte array
     * @return hexadecimal string
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
}